#include "scene.h"

BasicScene::BasicScene(QObject *parent) : QGraphicsScene (parent)
{
}
//---------------------------------------------------------------
void BasicScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button () == Qt::MidButton)
    {
        setZoomOriginal ();
    }
    QGraphicsScene::mousePressEvent(event);
}
//-----------------------------------------------------------
void BasicScene::wheelEvent (QGraphicsSceneWheelEvent *event)
{
//	event->accept ();
    if (event->modifiers ().testFlag (Qt::ControlModifier))
    {
        if (event->delta () < 0)
            incrZoomBy (0.9);
        else
            incrZoomBy (1.1);
    }
    else
        QGraphicsScene::wheelEvent (event);
}
//-------------------------------------
void BasicScene::incrZoomBy (const qreal z)
{
    foreach (QGraphicsView* view, views ()) {
        view->setTransformationAnchor (QGraphicsView::AnchorUnderMouse);
        view->scale (z, z);
    }
    emit zoomed (zoom ());
}
//-------------------------------------
void BasicScene::setZoomOriginal ()
{
    foreach (QGraphicsView* view, views ()) {
        view->setTransform (QTransform ());
        view->ensureVisible(0, 0, 10, 10);
    }
    emit zoomed (zoom ());
}
//-----------------------------------------------------
void BasicScene::setZoomBestFit (const QImage &image)
{
    QRectF rectF = image.rect ().adjusted (-25,-25,25,25);
    foreach (QGraphicsView* view, views ()) {
        view->fitInView (rectF, Qt::KeepAspectRatio);
    }
    emit zoomed (zoom ());
}
//-------------------------------
void BasicScene::setZoom(const qreal z)
{
    const QTransform& matrix (QTransform::fromScale (z, z));
    foreach (QGraphicsView* view, views ()) {
        view->setTransform (matrix);
    }
    emit zoomed (zoom ());
}
//-----------------------
qreal BasicScene::zoom ()
{
    if (views ().isEmpty ()) return 1;
    QGraphicsView* view = views().at (0);
    if (view == 0) return 1;
    const QTransform &matrix = view->transform ();
    return matrix.m11 ();
}

/**************************************************************************************************/
Scene::Scene (QGraphicsPixmapItem *item) : BasicScene (0)
{
    setPreviewPixmapItem (item);
    m_previewScene = item->scene ();
    m_pixmapItem = new QGraphicsPixmapItem ();
    addItem (m_pixmapItem);
    m_partList.clear ();
    clear ();
    setBackgroundBrush (QBrush (Qt::lightGray));
    m_partGroup = new QGraphicsItemGroup (0/*, this*/);
    m_rubber = new QRubberBand (QRubberBand::Rectangle);

    connect (this,
             SIGNAL(changed (QList<QRectF>)),
             this,
             SLOT(onChanged (QList<QRectF>)));
}
//-----------------
void Scene::clear ()
{
    for (int index = 0 ; index != m_partList.size () ; ++index) {
        removeItem (m_partList.takeAt (index));
    }

    m_pdf = false;
    m_partList.clear ();
    setZoomOriginal ();
    m_pixmapItem->setPixmap (QPixmap ());
    m_previewItem->setPixmap (QPixmap ());
    canUpdateParts (false);
    foreach (QGraphicsView* view, views ())
        view->setToolTip (tr ("Aucune image chargée"));

    m_pixmapItem->setPos (0,0);
}
//------------------------------------------------------
void Scene::setPreviewPixmap(const QPixmap &preview)
{
    m_previewScene->setSceneRect (preview.rect ());
    m_previewItem->setPixmap (preview);
}
//-----------------------------------------------------
void Scene::setOriginalPixmap(const QPixmap &original)
{
    setSceneRect (original.rect ());
    m_pixmapItem->setPixmap (original);
}
//-----------------------------------------------------------
void Scene::mousePressEvent (QGraphicsSceneMouseEvent *event)
{
    event->accept ();
    QPointF scenePos = event->scenePos ();

    QGraphicsItem* item = itemClicked (scenePos);
    if (not sceneRect ().contains (scenePos)
            || item == 0
            || event->button () != Qt::LeftButton)
    {
        BasicScene::mousePressEvent (event);
        return;
    }

    ImagePart* part = partAt (scenePos);

    if (part == 0) {
        startCreatingPart (event->screenPos (), event->scenePos ());
    }
    else {
        if (item == part) {
            if (!event->modifiers ().testFlag (Qt::ControlModifier)) unSelectAllParts ();
            part->setSelected (true);
            startMovingParts (event->scenePos (), event->pos ());
        }
        else {
            BasicScene::mousePressEvent (event);
        }
    }
}
//----------------------------------------------------------
void Scene::mouseMoveEvent (QGraphicsSceneMouseEvent *event)
{
    QPointF scenePos = event->scenePos ();

    if (not sceneRect ().contains (scenePos) or (event->buttons () != Qt::LeftButton)) {
        BasicScene::mouseMoveEvent (event);
        return;
    }

    event->accept ();

    switch (mode ()) {
    case MovingParts:
        movingParts (event->scenePos ());
    break;

    case CreatingPart:
        creatingPart (event->screenPos ());
    break;

    default:
        BasicScene::mouseMoveEvent (event);
    break;
    }

}
//-------------------------------------------------------------
void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    event->accept ();

    switch (mode ()) {
    case MovingParts:
        endMovingParts ();
    break;

    case CreatingPart:
        endCreatingPart (event->scenePos ());
    break;

    default:
    break;
    }

    setMode (NoAction);

    BasicScene::mouseReleaseEvent (event);
}
//--------------------------------------------
void Scene::keyReleaseEvent (QKeyEvent *event)
{
    // interception du relâchement de la touche <delete>
    if (event->key () == Qt::Key_Delete) {
        removeSelectedParts ();
    }
}
//------------------------------------------------------------------
void Scene::contextMenuEvent (QGraphicsSceneContextMenuEvent *event)
{
    QPointF scenePos = event->scenePos ();
    QGraphicsItem* item = itemClicked (scenePos);
    if (item == 0 or not sceneRect ().contains (scenePos)) {
        BasicScene::contextMenuEvent (event);
        return;
    }

    if (item == m_pixmapItem) {
        contextMenu (m_pixmapItem);
        return;
    }

    if (m_partList.contains ((ImagePart*) item)) {
        ImagePart* part = (ImagePart*) item;
        contextMenu (part);
    }
}
//----------------------------------------
void Scene::contextMenu (ImagePart *part)
{
    QMenu menu;

    QStyle* style = qApp->style ();

    QAction* action = new QAction (&menu);
    action->setText (tr ("Supprimer"));
    action->setData (0);
    action->setIcon (style->standardIcon (QStyle::SP_TrashIcon));
    menu.addAction (action);

    action = new QAction (&menu);
    action->setText (tr ("Changer le rôle"));
    action->setData (1);
    action->setIcon (style->standardIcon (QStyle::SP_BrowserReload));
    menu.addAction (action);

    if (part->isGreen ()) { // clic D sur un cadre vert
        menu.addSeparator ();
        action = new QAction (&menu);
        action->setText (tr ("Appliquer la ROC à ce cadre"));
        action->setData (2);
        action->setIcon (Config::appIcon ());
        menu.addAction (action);

        if (isPdf ()) { //  dans une page PDF
            action = new QAction (&menu);
            action->setText (tr ("Extraire le texte de ce cadre"));
            action->setData (3);
            action->setIcon (QIcon (":/images/gnome-mime-application-pdf.svg"));
            menu.addAction (action);
        }
        menu.addSeparator ();
        action = new QAction (&menu);
        action->setText (tr ("Réglages spécifiques..."));
        action->setData (4);
        action->setIcon (QIcon::fromTheme ("preferences-desktop", QIcon (":/images/preferences-desktop.svg")));
        menu.addAction (action);
    }

    if (greenList ().count () > 1 and part->order () != -1) {
        menu.addSeparator ();

        action = new QAction (&menu);
        action->setText (tr ("Définir le n° d'ordre dans l'affichage :"));
        action->setDisabled (true);
        menu.addAction (action);

        for (int index = 1 ; index <= lastPartOrder () ; ++index) {
            if (part->order () != index) {
                action = new QAction (&menu);
                action->setText (QString ("%1").arg (index));
                action->setData (index * 10);
                menu.addAction (action);
            }
        }
    }

    action = menu.exec (QCursor::pos ());

    if (action) {
        int index = action->data ().toInt ();
        switch (index) {
        case 0:
            removePart (part);
        break;

        case 1:
            setNextRole (part);
        break;

        case 2:
            emit askOCRFrom (part);
        break;

        case 3:
            emit askExtractionFrom (part->rectangle ());
        break;

        case 4:
            emit askPartSettingsOf (part);
        break;

        default:
            if (index >= 10) {
                swapPartOrder (part, index / 10);
            }
        }
    }
}
//-----------------------------------------
void Scene::setNextRole (ImagePart* part)
{
    part->setNextRole (nextPartOrder ());
    //    updateParts ();
}

//-----------------------------------------------
void Scene::contextMenu (QGraphicsPixmapItem* /*pi*/)
{
    QMenu menu;

    QStyle* style = qApp->style ();

    QAction* action = new QAction (&menu);
    action->setText (tr ("Informations sur l'image"));
    action->setData (-1);
    action->setIcon (style->standardIcon (QStyle::SP_FileDialogInfoView));
    menu.addAction (action);

    if (! views ()[0]->transform ().isIdentity ()) {
        menu.addSeparator ();
        action = new QAction (&menu);
        action->setText (tr ("Zoom 1:1"));
        action->setData (0);
        //		action->setIcon (style->standardIcon (QStyle::SP_TrashIcon));
        menu.addAction (action);
    }

    if (!m_partList.isEmpty ()) {
        menu.addSeparator ();

        if (!selectedItems ().isEmpty ()) {
            action = new QAction (&menu);
            action->setText (tr ("Supprimer les cadres sélectionnés"));
            action->setData (1);
            action->setIcon (style->standardIcon (QStyle::SP_TrashIcon));
            menu.addAction (action);
        }

        action = new QAction (&menu);
        action->setText (tr ("Supprimer tous les cadres"));
        action->setData (2);
        action->setIcon (style->standardIcon (QStyle::SP_TrashIcon));
        menu.addAction (action);

        menu.addSeparator ();

        action = new QAction (&menu);
        action->setText (tr ("Sélectionner tous les cadres"));
        action->setData (3);
        action->setIcon (QIcon::fromTheme ("edit-select-all", QIcon (":/images/edit-select-all.svg")));
        menu.addAction (action);

        action = new QAction (&menu);
        action->setText (tr ("Dé-sélectionner tous les cadres"));
        action->setData (4);
        action->setIcon (QIcon::fromTheme ("edit-clear", QIcon (":/images/edit-clear.svg")));
        menu.addAction (action);
    }

    action = menu.exec (QCursor::pos());

    if (action) {
        switch (action->data ().toInt ()) {
        case -1:
            emit askInformationsAboutCurrentImage ();
        break;

        case 0:
            setZoomOriginal ();
        break;

        case 1:
            removeSelectedParts ();
        break;

        case 2:
            removeAllParts ();
        break;

        case 3:
            selectAllParts ();
        break;

        case 4:
            unSelectAllParts ();
        break;

        default:
        break;
        }
    }
}
//----------------------------------------------------------------
void Scene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    event->accept ();
    QPointF scenePos = event->scenePos ();

    QGraphicsItem* item = itemClicked (scenePos);
    if (! sceneRect ().contains (scenePos) or item == 0 or event->button () != Qt::LeftButton) {
        BasicScene::mouseDoubleClickEvent (event);
        return;
    }

    if (m_partList.contains ((ImagePart*) item)) {
        ImagePart* part = (ImagePart*) item;
        setNextRole (part);
    }
}
//-----------------------------------------------------------------------
void Scene::startMovingParts (const QPointF& scenePos, const QPointF& pos)
{
    if (selectedItems ().isEmpty ()) return;
    m_partGroup = createItemGroup (selectedItems ());
    m_movingStartDeltaPos = scenePos - pos;
    setMode (MovingParts);
    QApplication::setOverrideCursor (Qt::OpenHandCursor);
}
//-------------------------------------------
void Scene::movingParts (const QPointF& pos)
{
    if (mode () != MovingParts) return;

    QRectF newRect (m_partGroup->boundingRect ());
    newRect.moveTopLeft (pos - m_movingStartDeltaPos);

    //    if (m_pixmapItem->boundingRect ().contains (newRect))
    m_partGroup->setPos (pos - m_movingStartDeltaPos);
}
//---------------------------
void Scene::endMovingParts ()
{
    m_movingStartDeltaPos = QPointF ();
    destroyItemGroup (m_partGroup);
    m_partGroup = 0;
    setMode (NoAction);
}
//------------------------------------------------------------------------------
void Scene::startCreatingPart (const QPointF& screenPos, const QPointF& scenePos)
{
    setMode (CreatingPart);
    m_creatingStartScreenPos = screenPos;
    m_creatingStartScenePos = scenePos;
}
//--------------------------------------------------
void Scene::creatingPart (const QPointF& screenPos)
{
    QRectF tmp (QRectF(m_creatingStartScreenPos, screenPos).normalized());
    QRectF geom (tmp.intersected (m_pixmapItem->boundingRect ()));

    m_rubber->setGeometry (geom.toRect ());
    m_rubber->show ();
}

//---------------------------------------------------
void Scene::endCreatingPart (const QPointF& scenePos)
{
    m_rubber->hide ();

    //************** ici gérer la création
    QRectF candidate = QRectF (m_creatingStartScenePos, scenePos).normalized ().intersected (m_pixmapItem->boundingRect ());

    qreal minSize = ImagePart::borderSize () * 3.0;
    if (candidate.width () >= minSize and candidate.height () >= minSize) {
        addPart (candidate.toRect ());
    }

    unSelectAllParts ();

    m_creatingStartScreenPos = QPointF ();
    m_creatingStartScenePos = QPointF ();
    setMode (NoAction);
}
//******************************************************************************
void Scene::onCreatePartsFrom (const QList<QRect> &redList, const QList<QRect> &greenList)
{

    canUpdateParts (false);
    removeAllParts ();

    int order = 1;

    foreach (const QRect &r, greenList) {
        ImagePart* part = new ImagePart (r, 0, order);
        m_partList << part;
        order++;
    }
    foreach (const QRect &r, redList) {
        ImagePart* part = new ImagePart (r, 1);
        m_partList << part;
    }

    foreach (ImagePart* part, m_partList) {
        addItem (part);
        part->setParentItem (m_pixmapItem);
    }

    canUpdateParts (true);
}

//-------------------------------
void Scene::updateParts ()
{
    if (not canUpdateParts ()) return;

    emit rectsHaveChanged (toRedRects (), toGreenRects ());

}
//*******************************************************
//*******************************************************
ImagePart* Scene::addPart (const QRect& r)
{
    int order = nextPartOrder ();

    ImagePart* part = new ImagePart (r);
    part->setOrder (order);
    addItem (part);
    part->setParentItem (m_pixmapItem);
    part->setSelected (true);

    m_partList << part;
    return part;
}
//--------------------------------
void Scene::removeSelectedParts ()
{
    foreach (QGraphicsItem* item, selectedItems ()) {
        ImagePart* part = (ImagePart*) item;
        removePart (part);
    }
}
//---------------------------------------
void Scene::removePart (ImagePart* part)
{
    int index = m_partList.indexOf (part);
    if (index < 0) return;
    removePart (index);
}
//--------------------------------
void Scene::removePart (const int index)
{
    removeItem (m_partList.takeAt (index));
    updateParts ();
}
//------------------------------------------
void Scene::removeAllParts ()
{
    while (!m_partList.isEmpty ()) removeItem (m_partList.takeFirst ());

    updateParts ();
}
//-------------------------------
void Scene::setMode (const int newMode)
{
    m_mode = newMode;

    switch (newMode) {
    case NoAction:
        QApplication::restoreOverrideCursor ();
        updateParts ();
    break;

    case MovingParts:
        QApplication::setOverrideCursor (Qt::OpenHandCursor);
    break;

    case CreatingPart:
        QApplication::setOverrideCursor (Qt::CrossCursor);
    break;

    default:
    break;
    }
}
//-------------------------------------
QList<ImagePart *> Scene::greenList()
{
    QList<ImagePart*> result;
    QMap<int, ImagePart*> tmp;

    foreach (ImagePart* part, m_partList)
        if (part->isGreen ()) tmp.insert (part->order (), part);

    int order = 1;

    foreach (ImagePart* part, tmp.values ()) {
        part->setOrder (order);
        result << part;
        ++order;
    }

    return result;
}
//------------------------------------
QList<ImagePart *> Scene::redList()
{
    QList<ImagePart*> result;

    foreach (ImagePart* part, m_partList)
        if (part->isRed ()) result << part;

    return result;
}
//---------------------------
int Scene::nextPartOrder()
{
    return greenList ().size () + 1;
}
//----------------------------------------------
void Scene::onChanged (const QList<QRectF> & /*region*/)
{
    //    updateParts ();
}
//----------------------------
void Scene::selectAllParts ()
{
    foreach (ImagePart* part, m_partList) {
        part->setSelected (true);
    }
}
//-------------------------------------------------
ImagePart* Scene::partAt (const QPointF &scenePos)
{
    ImagePart* result = 0;

    foreach (ImagePart* part, m_partList) {
        if (part->boundingRectToScene ().contains (scenePos)) {
            if (result == 0 || part->zValue () >= result->zValue ())
                result = part;
        }
    }

    return result;
}
//----------------------------------------
void Scene::setFirstPart (ImagePart* part)
{
    swapPartOrder (part, 1);
}
//----------------------------------------
void Scene::setLastPart (ImagePart* part)
{
    swapPartOrder (part, greenList ().size ());
}
//--------------------------------------------------------
void Scene::swapPartOrder (ImagePart* part1, const int newOrder)
{
    int order = part1->order ();
    if (order < 0 || newOrder == order) return;

    ImagePart* part2 = greenList ().at (newOrder - 1);	// le cadre qui a ce n° d'ordre
    part1->setOrder (newOrder);
    part2->setOrder (order);
    updateParts ();
}
//***************************************************************************
